function [coefc, coefs, msglen, etotal, cb, db] = embed_image2(img, settings, msg)
%EMBED_IMAGE2 embed message into JPEG image using MME.
% Usage :
%  [COEFC, COEFS, MSGLEN, ETOTAL, CB, DB] = EMBED_IMAGE2(IMG, SETTINGS, MSG)
%
% Input :
%   IMG indicates original DCT coeffients.
%   SETTINGS is a struct, with the following key-value pairs.
%      cmp_coef - coeffients from cover image, for debug only.
%      k  - size of message block (in bits).
%      t  - maximum number of bits to change in N(=2^K-1) bits of
%      coeffients.
%      rands - random stream to generate pusedo-random numbers.
%      qtable - 8x8 matrix used in JPEG compression.
%      typ / arg - message length in relation to capacity.
%      All available TYP arguments are:
%        'bpc' : ARG specifies the maximum value of bpc, a value in [0..1].
%        'bpp' : ARG specifies the maximum value of bpp, a value in [0..1].
%        'bit' : ARG specifies the maximum number bits of message.
%      method - for choosing different embedding algorithm.
%        -3 - Other (Shrink  )       (E = ?, C = ?)
%        -2 - Shrink allowed MME     (E = ?, C = +/-1)
%        -1 - Adjust value in reference to cmp_coef.
%        0 - Original MME            (E = R, C = +/-1 ~= 0)
%        1 - Modified MME (default)  (E = R*Q, C = +/-1 ~= 0)
%        2 - Modified MME            (E = R*Q^2, C = +/-1 ~= 0)
%        3 - Other                   (E = ?, C = ?)
%           specify error_func / change_func
%      error_func - (default : @get_LSB_error)
%      change_func - (default : @get_changed_value_orig)
%      debug - run debug code.
%      verbose - 0 no debug output.
%                1 display debug info.(default : 0)
%      zlist - a list of all possible solution, only related to k & t.
%   MSG is a series of values between 0..255 to embed.
%
% Output:
%   COEFC / COEFS denotes new DCT coeffients after embedding.
%   MSGLEN is the number of bytes of message embeded.
%   ETOTAL is the overall error got from IMG.
%   CB is the bytes of coeffients changed.
%   DB is the bytes of coeffients in degeneration, where original MMEx
%   (call GET_CHANGED_VALUE_ORIG), DB should always be 0.
%
% Notes:
%   RANDS is used in the following order:
%     1. randperm       : rearrange the order of JPEG coeffients.
%     2. bitxor message : mess up with the message
%   To decode the image, RANDS should be used in the same order with same
%   SEED set.
%

if nargin < 3
    error('embed_image2:invalidArg', 'Insufficient number of input arguments.');
end
if ~isfield(settings, 'method')
    settings.method = 1;
end
if ~isfield(settings, 'debug')
    settings.debug = 0;
end
if ~isfield(settings, 'verbose')
    settings.verbose = 0;
end

%% DCT
%  O  -> c'           (non-round DCT coeffients)
O = nonround_dct(img, settings.qtable);
% sym_o -> original symbol (original coeffients c'')
sym_o = round(O);
% r_o   -> R (c''-c') (rounding errors)
% r_c -> S - C'       (rounding errors + embedding errors)
% sym_c -> changed symbol (changed coeffients)
% r_c = sym_c - O;

%% DEBUG: check coeffients
coefc = sym_o;
if settings.debug
    % The coeffients calculated from original image (COEF0) is different
    % from the one from JPEG_READ (CMP_COEF)
    check_compressor(coefc, cmp_coef);
end

%% calculate errors
if (settings.method == -1)
    ref_o = cmp_coef;
else
    ref_o = sym_o;
end

r_o = ref_o - O;
if ~isfield(settings, 'change_func')
    settings.change_func = @get_changed_value_orig;
end
if ~isfield(settings, 'error_func')
    settings.error_func = @get_LSB_error;
end

switch (settings.method)
    case 0
        settings.error_func = @get_LSB_error;
    case 1
        settings.error_func = @get_q_error;
    case 2
        settings.error_func = @get_q_error2;
    case -2
        settings.change_func = @get_changed_value;
end

if settings.verbose
    fprintf(1, 'error_func :%s\n',functiontostring(settings.error_func));
    fprintf(1, 'change_func:%s\n',functiontostring(settings.change_func));
end
err = settings.error_func(r_o, ref_o, settings.qtable);
sym_c = settings.change_func(ref_o, r_o);

%% skip DC
[M0, N0] = size(sym_o);
sym_o = im2vec(ref_o,[8 8])';
sym_c = im2vec(sym_c,[8 8])';
err = im2vec(err, [8 8])';
ac_o = sym_o(:,2:end);
ac_c = sym_c(:,2:end);
ac_e = err(:,2:end);

%% random embedding order
[M, N] = size(ac_o);
order = prand3(M*N, settings.rands);
%order = prand1(M*N, rands); % DEBUG only, returned order is [1..M*N;1..M*N]
ac_o = ac_o(order(1,:));
ac_c = ac_c(order(1,:));
ac_e = ac_e(order(1,:));
ind = find(ac_o ~= 0); % nonzero DCT
ind_len = length(ind); % number of nonzero DCT coeffients

%% generate binary sequence from original message
bin = arrayToBinary(msg, settings.verbose);
bin_len = numel(bin);
if mod(bin_len, settings.k) ~= 0
    % append 0 to BIN, size of BIN should be multiple times of k
    old_len = bin_len;
    bin_len = ceil(bin_len / settings.k) * settings.k;
    bin = [bin zeros(1,bin_len - old_len)];
end

%% bitxor message with random sequence
bin = mod(bin(:) + round(settings.rands.rand(bin_len,1)), 2);

%% calc maximum embed rate
switch (settings.typ)
    case 'bpc'
        if (settings.arg < 0) || (settings.arg > 1)
            error('ARG should be a float value in [0..1].');
        end
        max_bit = ceil(ind_len * settings.arg);
    case 'bpp'
        if (settings.arg < 0) || (settings.arg > 1)
            error('ARG should be a float value in [0..1].');
        end
        max_bit = ceil(M0*N0*settings.arg);
    case 'bit'
        if (settings.arg < 0)
            error('ARG should be a float value above 0.');
        end
        max_bit = ceil(settings.arg);
    otherwise
        error('Unknown TYP value.');
end

%% enumerate all possible situations
if ~isfield(settings, 'zlist')
    % generate all possible Z out of loop to speedup,
    % set it in embed_dir2 to further improve speed.
    settings.zlist = genzlist(settings.t, settings.k);
end

n = 2^settings.k - 1;

%% initialize loop value
etotal = 0; % total rounding error
cb = 0; % total change coeffients
db = 0; % total degenerate coeffients
msg_count = 1; % bits of message embeded
i = 1; % number of used coeffients

%% start embeding
while (i <= ind_len)
    nexti = i + n;  % n bit in a coef block
    if (nexti - 1 > ind_len)
        % skip the last several coeffients & stop embedding
        warning('Not enough space to embed the whole message. %d/%d',msg_count,bin_len);
        break;
    end
    
    %initial value
    current_msg = bin(msg_count:msg_count+settings.k-1);
    current_coef = ac_o(ind(i:i+n-1));
    current_error = ac_e(ind(i:i+n-1));
    current_symbol = ac_c(ind(i:i+n-1));
    [nc, pos, emin] = mme_embedpart2(settings.zlist, current_coef, current_error, current_symbol, current_msg, settings.k);% embed
    
    if settings.method <= -2
        degenerate_bits = (nc == 0);%ALWAYS TRUE: current_coef ~= 0
        degenerate = any(degenerate_bits);
        if settings.debug
            for j = i:i+n-1
                fprintf(1,'%d ',ind(j));
            end
            fprintf(1,'\t');
            debug_output(settings.n, current_coef, nc, settings.k, current_msg, degenerate, emin);
        end
        
        while degenerate
            % some Values change from 1 or -1 to 0
            % 1. set theses values to 0, change db
            num_degenerate = sum(degenerate_bits);
            
            if (nexti - 1 + num_degenerate > ind_len)
                warning('Not enough space to embed the whole message. %d/%d',msg_count,bin_len);
                break;
            end
            db = db + num_degenerate;
            tmp = ac_o(ind(i:i+n-1));
            tmp(degenerate_bits) = 0;
            ac_o(ind(i:i+n-1)) = tmp;
            
            % 2. choose some more coefs (change:ind, nexti)
            new_ind = ind(i:i+n-1);
            new_ind = [new_ind(~degenerate_bits), ind(nexti:nexti+num_degenerate-1)];
            assert(numel(new_ind) == n);
            ind(i:i+n-1) = new_ind;
            nexti = nexti + num_degenerate;
            
            % 3. redo embed
            %current_msg = bin(msg_count:msg_count+k-1);
            current_coef = ac_o(ind(i:i+n-1));
            current_error = ac_e(ind(i:i+n-1));
            current_symbol = ac_c(ind(i:i+n-1));
            [nc, pos, emin] = mme_embedpart2(settings.zlist, current_coef, current_error, current_symbol, current_msg, k);% embed
            degenerate_bits = (current_coef ~= 0) & (nc == 0);
            degenerate = any(degenerate_bits);
            if settings.debug
                for j = i:i+n-1
                    fprintf(1,'%d ',ind(j));
                end
                fprintf(1,'\t');
                debug_output(n, current_coef, nc, k, current_msg, degenerate, emin);
            end
        end
        
        if degenerate
            % when only no more coeffients
            break;
        end
    end
    
    % update coeffients
    ac_o(ind(i:i+n-1)) = nc;

    assert(isequal(pos, (nc ~= current_coef)));
    etotal = etotal + emin;
    msg_count = msg_count + settings.k;
    cb = cb + sum(pos(:));
    i = nexti;
    if (msg_count >= bin_len) || (msg_count >= max_bit)
        % already embeded whole message or reach maximum rate
        break;
    end
end

ac_o = ac_o(order(2,:));% back to original order
sym_o(:,2:end) = reshape(ac_o,[M N]);
coefs = vec2im(sym_o',[0 0],[8 8],M0/8,N0/8);
msglen = msg_count;

if settings.debug
    assert(numel(coefc) == numel(coefs));
    %check_compressor(coefc, coefs);
end

if settings.verbose
    fprintf(1, 'image size = %d*%d px,  nonzero AC coef = %d\n', M0, N0, ind_len);
    fprintf(1, '%d / %d / %d of nonzero AC coef changed / degenerate / used \n',cb, db, i);
    fprintf(1, 'embeded message len = %db (%0.3f bpac, %0.3f bpp)\n', msg_count, msg_count / ind_len, msg_count / (M0*N0));
    fprintf(1, 'total rounding error = %0.2f\n', etotal);
end

end
